package com.ht.web_vr_home_tour_backend.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.xiaoymin.knife4j.core.util.StrUtil;
import com.ht.web_vr_home_tour_backend.config.data.RedisConstants;
import com.ht.web_vr_home_tour_backend.dao.ObjInfoDao;
import com.ht.web_vr_home_tour_backend.dao.OssImpl;
import com.ht.web_vr_home_tour_backend.obj.enums.ObjType;
import com.ht.web_vr_home_tour_backend.obj.pojo.ObjInfo;
import com.ht.web_vr_home_tour_backend.obj.vo.ObjInfoVo;
import com.ht.web_vr_home_tour_backend.utils.CacheClient;
import com.ht.web_vr_home_tour_backend.utils.RedisIdWorker;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.stream.Collectors;

@Service
public class ObjInfoService extends ServiceImpl<ObjInfoDao, ObjInfo> {
    private final CacheClient cacheClient;
    private final OssImpl ossImpl;
    private final RedisIdWorker redisIdWorker;

    public ObjInfoService(CacheClient cacheClient, OssImpl ossImpl, RedisIdWorker redisIdWorker) {
        this.cacheClient = cacheClient;
        this.ossImpl = ossImpl;
        this.redisIdWorker = redisIdWorker;
    }

    /**
     * 根据id获取对象信息
     *
     * @param id 对象信息id
     * @return 对象信息
     */
    public ObjInfo getObjInfoById(Long id) {
        return cacheClient.queryWithMutex(
                ObjInfo.class,
                RedisConstants.tb_ObjInfo_Id_keyPrefix,
                id.toString(),
                this::getById,
                RedisConstants.tb_Cache_Base_TTL
        );
    }

    /**
     * 通过链接获取对象信息
     *
     * @param link 对象链接
     * @return 对象信息
     */
    public ObjInfo getObjInfoByLink(String link) {
        return cacheClient.queryWithMutex(
                ObjInfo.class,
                RedisConstants.tb_ObjInfo_Link_keyPrefix,
                link,
                (_link) -> this.getOne(this.lambdaQuery()
                        .eq(ObjInfo::getObjLink, _link)
                        .last("limit 1")
                        .getWrapper()),
                RedisConstants.tb_Cache_Base_TTL
        );
    }

    /**
     * 获取对象
     *
     * @param link 对象链接
     * @return 对象
     */
    public InputStream getObjByLink(String link) {
        return ossImpl.download(link);
    }

    /**
     * 分页获取指定用户上传的对象
     *
     * @param current 当前页码
     * @param size    当前页大小
     * @param type    对象类型（可选）
     * @param userId  指定的用户
     * @return 分页的对象数据
     */
    public Page<ObjInfoVo> pageObj(long current, long size, String type, String userId) {
        Page<ObjInfo> page = new Page<>(current, size);
        LambdaQueryChainWrapper<ObjInfo> query = this.lambdaQuery();
        if (StrUtil.isNotBlank(userId) && NumberUtil.isLong(userId)) {
            query = query.eq(ObjInfo::getCreateUser, userId);
        }
        if (StrUtil.isNotBlank(type)) {
            query = query.eq(ObjInfo::getCreateUser, userId);
        }
        this.page(page, query.getWrapper());
        Page<ObjInfoVo> dtoPage = new Page<>();
        BeanUtil.copyProperties(page, dtoPage);
        dtoPage.setRecords(
                page.getRecords()
                        .stream()
                        .map(ObjInfoVo::new)
                        .collect(Collectors.toList())
        );
        return dtoPage;
    }

    /**
     * 上传对象
     *
     * @param file 上传的对象
     */
    @Transactional
    public ObjInfo uploadObj(ObjType type, MultipartFile file) {
        String name = file.getOriginalFilename();
        ObjInfo objInfo = new ObjInfo();
        objInfo.setObjName(name);
        objInfo.setId(redisIdWorker.nextId(RedisConstants.icr_ObjInfo_keyPrefix));
        objInfo.setObjType(type);
        String hash = null;
        try {
            hash = ossImpl.upload(file.getInputStream());
            objInfo.setObjLink(hash);
            this.save(objInfo);
        } catch (IOException e) {
            throw new IORuntimeException(e);
        }
        return objInfo;
    }

    /**
     * 删除对象
     * @param objId 对象id
     */
    @Transactional
    public void deleteObj(long objId) {
        String objLink = this.getObjInfoById(objId).getObjLink();
        this.removeById(objId);
        boolean exists = this.exists(
                this.lambdaQuery()
                        .eq(ObjInfo::getObjLink, objLink)
                        .getWrapper()
        );
        // 防止出现多个对象信息引用同一个对象
        if (!exists) {
            ossImpl.delete(objLink);
        }
        cacheClient.dropCache(
                RedisConstants.tb_ObjInfo_Id_keyPrefix,
                Long.toString(objId)
        );
    }

    /**
     * 更新对象数据
     * @param objInfo 新对象数据
     */
    @Transactional
    public void updateObjInfo(ObjInfo objInfo) {
        objInfo.setCreateTime(null);
        objInfo.setCreateUser(null);
        this.updateById(objInfo);
        cacheClient.dropCache(
                RedisConstants.tb_ObjInfo_Id_keyPrefix,
                String.valueOf(objInfo.getId())
        );
    }
}
